﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core;
using Cofe.Core.Property;
using System.Reflection;
using Cofe.Core.Utils;
using System.Collections.Concurrent;
using System.ComponentModel.Composition;
using Cofe.Core.Filter;

namespace Cofe.Data
{
    public class DatabasePropertyCache : MemoryPropertyCache
    {
        #region Constructor

        //[Export(typeof(ICofeService))]
        //[ServicePriority(ServicePriorityAttribute.DefaultPriority_COFEDB)]
        public new class Factory : IPropertyCacheFactory
        {
            private static MemoryPropertyCache.Factory MemoryCacheFactory = new MemoryPropertyCache.Factory();

            public virtual IPropertyCache CreatePropertyCache(ParsableType pathType, string path)
            {
                if (pathType != ParsableType.ParsePath)
                    throw new NotSupportedException();

                var newCache = new DatabasePropertyCache();
                newCache.PropertyCacheDictionary.SetProperty(PropertyPair.FromValue(CofeProperties.FullParseName, path));
                return newCache;
            }

            public virtual IPropertyCache CreatePropertyCache()
            {
                return new DatabasePropertyCache();
            }

            public virtual IPropertyCache CreatePropertyCache(IPropertyCache[] propertyCaches)
            {
                return MemoryCacheFactory.CreatePropertyCache(propertyCaches);                
            }

            public virtual ParsableType SupportedParsableType
            {
                get { return ParsableType.ParsePath | ParsableType.Guid; }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dbEntry">If null, obtain it when it's needed (provided that parseName/Id set)</param>
        public DatabasePropertyCache(DatabaseEntry dbEntry = null) 
            : base()
        {
            _dbEntry = dbEntry;
            if (DatabasePropertyCache._getPropertyFromRepository == null)
                throw new Exception();
        }

        #endregion

        #region Methods


        private static MethodInfo _getPropertyFromRepository = typeof(DatabasePropertyCache)
            .GetMethods(BindingFlags.Public | BindingFlags.Static).First
                (mi => mi.Name == "getPropertyFromRepository" && mi.GetGenericArguments().Length == 1);

        public static PropertyPair getPropertyFromRepository<T>(IRepository repository, DatabaseEntry dbEntry, object property)
        {
            T value = repository.GetValue<T>(dbEntry, property);
            return PropertyPair.FromValue(property, value);
        }


        protected DatabaseEntry checkDbEntry()
        {
            if (_dbEntry == null)
            {
                if (PropertyCacheDictionary.GetCachedProperties().Contains(CacheProperties.Id))
                    _dbEntry = new DatabaseEntry() { Id = PropertyCacheDictionary.GetProperty(CacheProperties.Id).ValueAs<Guid>() };
                else if (PropertyCacheDictionary.GetCachedProperties().Contains(CofeProperties.FullParseName))
                {
                    using (var repository = CofeDBServices.RepositoryFactory.CreateRepository())
                        _dbEntry = repository.FindDbEntry(FilterCollections.FromParseName(
                            PropertyCacheDictionary.GetProperty(CofeProperties.FullParseName).ValueAsString)).FirstOrDefault();
                }
            }
            return _dbEntry;
        }

        protected override PropertyPair getProperty(object property)
        {
            PropertyPair retVal = base.getProperty(property);
            //retVal = null;

            if (retVal == null & checkDbEntry() != null)
            {                
                using (var repository = CofeDBServices.RepositoryFactory.CreateRepository())
                {                    
                    var defType = CofeServices.PropertyDictionary.AttributeHelper.GetDefaultType(property);
                    var mi = _getPropertyFromRepository.MakeGenericMethod(defType);
                    retVal = (PropertyPair)mi.Invoke(null, new object[] { repository, _dbEntry, property });
                }

                if (retVal != null)
                    PropertyCacheDictionary.CacheProperty(retVal);
            }

            return retVal;
        }


        public override bool IsPropertySupported(object property)
        {
            if (DBEntry != null)
            {
                if (dbEntryProperties.Contains(property))
                    return true;
            }

            return base.IsPropertySupported(property);
        }

        #endregion

        #region Data

        private static object[] dbEntryProperties = new object[]
            {
                 CacheProperties.ParentId,
                            CacheProperties.Id,
                            CacheProperties.SupportedDefinitions,
                            CacheProperties.SupportedProperties,
                            CacheProperties.LastCachedTimeUtc,
                            CacheProperties.LastListTimeUtc,
                            CofeProperties.FullParseName,
                            CofeProperties.Label,
                            CofeProperties.DefaultInterfaceTypeString, 
                            //CofeEntryTypeProperties.EntryTypeInfoId        
            };
        private DatabaseEntry _dbEntry = null;

        #endregion

        #region Public Properties

        public DatabaseEntry DBEntry { get; internal set; }
       

        #endregion
    }

}
